package cn.zhb.backend.annotation.aspect;

import cn.zhb.backend.annotation.SyncLock;
import cn.zhb.backend.context.BaseContext;
import cn.zhb.backend.exception.ExceptionMessage;
import cn.zhb.backend.exception.SystemException;
import cn.zhb.backend.utils.LockService;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/***
 *@Description
 *@Author ZHB
 *@Create 2025/5/11 下午3:36
 **/
@Aspect
@Component
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE) // 确保先于@Transactional执行
public class SyncLockAspect {
    private final LockService lockService;  // 简单的锁服务

    @Around("@annotation(cn.zhb.backend.annotation.SyncLock)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        SyncLock syncLock = signature.getMethod().getAnnotation(SyncLock.class);

        // 1. 构建锁key
        String lockKey = buildLockKey(syncLock, joinPoint);

        // 2. 尝试加锁
        if (!lockService.tryLock(lockKey, syncLock.timeout()))
            throw new SystemException(ExceptionMessage.SYNC_ERROR);

        try {
            // 3. 执行业务方法 在锁内执行，事务切面会在内部处理
            return joinPoint.proceed();
        } finally {
            // 4. 释放锁
            lockService.unlock(lockKey);
        }
    }

    private String buildLockKey(SyncLock syncLock, ProceedingJoinPoint joinPoint) {
        // 基本key
        String key = StringUtils.isBlank(syncLock.key()) ?
                joinPoint.getSignature().toShortString() :
                syncLock.key();

        // 添加用户标识
        if (StringUtils.isNotBlank(syncLock.userKey())) {
            Long userId = BaseContext.getCurrentUserId();
            key += ":user:" + userId;
        }

        return key;
    }
}
